﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.Net;

namespace Agility.Wcf.ClientProxy
{
    public class CookieManagerMessageInspector : IClientMessageInspector
    {
        private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
        private static readonly CookieManagerMessageInspector instance = new CookieManagerMessageInspector();
        
        private CookieManagerMessageInspector()
        {
            logger.Debug("--> Entering ctor");
            logger.Debug("<-- Entering ctor");
        }

        public static CookieManagerMessageInspector GetInstance()
        {
            logger.Debug("--> Entering GetInstance");
            logger.Debug("<-- Leaving GetInstance");
            return instance;
        }
        
        public string AuthenticationCookie { get; set; }

        #region IClientMessageInspector implementation

        public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            logger.Debug("--> Entering AfterReceiveReply");
            HttpResponseMessageProperty response = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
            if (response != null)
            {
                // get the cookie from the response header
                string cookie = response.Headers["Set-Cookie"];
                logger.Debug("Extracted the cookie from the response");
                // if a cookie was actually sent update the shared cookie
                if (!string.IsNullOrEmpty(cookie))
                {
                    this.AuthenticationCookie = cookie;
                    logger.Debug("Updated the authentication cookie");
                    logger.Trace("Cookie: {0}", this.AuthenticationCookie);
                }
            }
            logger.Debug("<-- Leaving AfterReceiveReply");
        }

        public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
        {
            logger.Debug("--> Entering BeforeSendRequest");
            HttpRequestMessageProperty httpRequest;

            // make sure to have an HttpRequest available
            if (!request.Properties.ContainsKey(HttpRequestMessageProperty.Name))
            {
                request.Properties.Add(HttpRequestMessageProperty.Name, new HttpRequestMessageProperty());
                logger.Debug("Added the HttpRequest to the message");
            }

            if (!string.IsNullOrEmpty(this.AuthenticationCookie))
            {
                // add the shared cookie to the message header
                httpRequest = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
                string formattedCookie = this.FormatCookie();
                httpRequest.Headers.Add(HttpRequestHeader.Cookie, formattedCookie);
                logger.Debug("Added the cookie to the request header");
                logger.Trace("Cookie: {0}", formattedCookie);

            } 
            logger.Debug("<-- Leaving BeforeSendRequest");
            return null;
        }

        private string FormatCookie()
        {
            StringBuilder cookieBuilder = new StringBuilder();

            // split the cookie into it's parts
            string[] cookieCrumbs = this.AuthenticationCookie.Split(new char[] {';', ','});
            foreach (string crumb in cookieCrumbs)
            {
                // ignore the 'path' and 'expires' parts and anything that does not conform to a 'key=value' notification
                if (crumb.IndexOf('=') > 0 && !crumb.Trim().StartsWith("path") && !crumb.Trim().StartsWith("expires"))
                {
                    cookieBuilder.Append(crumb).Append("; ");
                }
            }
            // remove the excessive '; ' at the end
            cookieBuilder.Remove(cookieBuilder.Length - 2, 2);
            return cookieBuilder.ToString();
        }

        #endregion
    }
}
